package bootstrap

import (
	"github.com/kataras/iris"
	"github.com/kataras/iris/middleware/logger"
	"github.com/kataras/iris/middleware/recover"
	"imooc.com/demo-lottery/conf"
	"imooc.com/demo-lottery/cron"
	"io/ioutil"
	"time"
)

/*
类似继承示例
type Engine interface {
    Start()
    Stop()
}

type Car struct {
    Engine
}
*/
// （Bootstrapper继承和共享 iris.Application ）
// 参考文章： https://hackthology.com/golangzhong-de-mian-xiang-dui-xiang-ji-cheng.html
type Bootstrapper struct {
	// 没有名字，意思是继承，就是bootstrapper继承*iris.application这个类
	// 从左边小图标可以看出，bootstrapper继承application的所有方法
	*iris.Application

	AppName      string
	AppOwner     string
	AppSpawnDate time.Time
	//Sessions *sessions.Sessions
}

type Configurator func(*Bootstrapper)

func New(appName, appOwner string,
	cfgs ...Configurator) *Bootstrapper {

	b := &Bootstrapper{
		Application:  iris.New(),
		AppName:      appName,
		AppOwner:     appOwner,
		AppSpawnDate: time.Now(),
		//Sessions:     nil,
	}

	for _, cfg := range cfgs {
		cfg(b)
	}

	return b
}

func (b *Bootstrapper) SetupViews(viewDir string) {
	// layout基于 viewDir 路径后面
	// viewDir/shared/layout.html
	htmlEngine := iris.HTML(viewDir, ".html").
		Layout("shared/layout.html")

	// 每个请求重新加载模板
	// 开发模式打开，线上关闭
	htmlEngine.Reload(true)

	//
	htmlEngine.AddFunc("FromUnixtimeShort", func(t int) string {
		dt := time.Unix(int64(t), 0)
		return dt.Format(conf.SysTimeFormShort)
	})

	htmlEngine.AddFunc("FromUnixtime", func(t int) string {
		dt := time.Unix(int64(t), 0)
		return dt.Format(conf.SysTimeForm)
	})

	b.RegisterView(htmlEngine)
}

// SetupErrorHandlers prepares the http error handlers
// `(context.StatusCodeNotSuccessful`,  which defaults to < 200 || >= 400
// but you can change it).
func (b *Bootstrapper) SetupErrorHandlers() {
	b.OnAnyErrorCode(func(ctx iris.Context) {
		err := iris.Map{
			"app":     b.AppName,
			"status":  ctx.GetStatusCode(),
			"message": ctx.Values().GetString("message"),
		}
		// 有json参数，就用json格式输出
		if jsonOutPut := ctx.URLParamExists("json"); jsonOutPut {
			ctx.JSON(err)
			return
		}
		// 没有json就用模板输出
		ctx.ViewData("Err", err)
		ctx.ViewData("Title", "Error")
		ctx.View("shared/error.html")

	})
}

// Configure accepts configurations and
// runs them inside the Bootstraper's context.
func (b *Bootstrapper) Configure(cs ...Configurator) {
	for _, c := range cs {
		c(b)
	}
}

func (b *Bootstrapper) setupConn() {
	cron.ConfigureAppOneCron()
}

const (
	// StaticAssets is the root directory for public assets like images,
	// css, js.
	StaticAssets = "./public"
	// Favicon is the relative 9to the "StaticAssets")
	// favicon path for our app.
	Favicon = "/favicon.ico"
)

// Bootstrap prepares our application.
//
// Returns itself.
// 在外面只要调用bootstrap就可以把站点各种配置信息设置好
func (b *Bootstrapper) Bootstrap() *Bootstrapper {
	b.SetupViews("./views")
	b.SetupErrorHandlers()

	// static files

	// Favicon提供静态图标
	//接受2个参数，第二个是可选的
	// favPath（string），声明 __.ico 的系统目录路径
	// requestPath（string），它是路由的路径，默认情况下这是“/favicon.ico”，因为有些浏览器首先会尝试默认获取，
	//如果您有多个favicon（桌面，移动设备等），您可以声明自己的路径

	//这个函数会为你添加一个路由，它会将/yuorpath/yourfile.ico静态地提供给/yourfile.ico
	//（没有什么特别的，你自己无法处理）。
	//请注意，您必须在必须自动提供的每个favicon（桌面，移动设备等）上调用它。

	// Returns the GET *Route.  返回GET * Route
	b.Favicon(StaticAssets + Favicon)
	// StaticWeb returns a handler that serves HTTP requests
	// with the contents of the file system rooted at directory.
	// StaticWeb返回一个服务于HTTP请求的处理程序
	//使用以目录为根的文件系统的内容。

	//第一个参数：路由路径
	//第二个参数：系统文件目录
	//第三个OPTIONAL参数：异常路由
	//（=优先考虑这些路由而不是静态处理程序）
	//查看更多选项 app.StaticHandler。

	//app.StaticWeb("/static", "./static")
	//上面第二参数 表示静态文件在 main.go 同级目录 static 里面
	//第一个参数表示请求路由为static  例如 请求路由是 host/static/xxx.js

	//作为一种特殊情况，返回的文件服务器会重定向任何请求
	//以“/index.html”结尾到同一路径，没有最终版本
	//“index.html”

	// StaticWeb调用StaticHandler（systemPath，listingDirectories：false，gzip：false）。
	//
	// Returns the GET *Route.  返回GET * Route
	b.StaticWeb(StaticAssets[1:], StaticAssets)

	indexHtml, err := ioutil.ReadFile(StaticAssets + "/index.html")
	if err == nil {
		b.StaticContent(StaticAssets[1:]+"/", "text/html",
			indexHtml)
	}
	// 不要把目录末尾"/"省略掉
	iris.WithoutPathCorrectionRedirection(b.Application)

	b.setupConn()

	b.Use(recover.New())
	b.Use(logger.New())

	return b
}

// Listen starts the http server with the specified "addr".
func (b *Bootstrapper) Listen(addr string, cfgs ...iris.Configurator) {
	b.Run(iris.Addr(addr), cfgs...)
}
